闭包相关 Closure

Question 1(基础题 / Fundamental)

中文: 什么是闭包(Closure)?请用你自己的话解释一下,并举一个简单例子。

English: What is a closure in JavaScript? Explain it in your own words and give a simple example.

 

中文: 闭包是指:函数可以访问并记住它定义时所在的作用域,即使这个函数在其作用域之外执行

English: A closure is a function that remembers and can access the variables from its lexical scope even when it is executed outside that scope.

 

Question 2(理解加深 / Deeper Understanding)

中文: 为什么 JavaScript 会产生闭包?是语言“设计出来的”,还是“自然形成的”?

English: Why do closures exist in JavaScript? Are they intentionally designed, or do they occur naturally?

 

中文: 闭包不是一个“额外设计的特性”,而是 JavaScript 词法作用域(Lexical Scope)的自然结果

闭包的本质不是“特殊机制”,而是 作用域链 + 垃圾回收机制共同作用的结果

English: Closures are not an extra feature intentionally added — they are a natural result of JavaScript’s lexical scoping.

A closure is the continuation of lexical scope beyond the function’s execution lifecycle.


什么是词法作用域(关键铺垫)

中文: JavaScript 在函数定义的时候就确定了作用域,而不是在执行的时候。

也就是说: 函数能访问哪些变量,在写代码的时候就已经决定了。

English: In JavaScript, scope is determined at function definition time, not at runtime.

That means: What variables a function can access is fixed when the code is written.


闭包是怎么“自然出现”的

看这个例子:

中文解释:

  1. inner 在定义时,就“记住”了它的作用域(包含 x
  2. 即使 outer 执行完了
  3. inner 仍然要访问 x
  4. 所以 JS 引擎必须把 x 留在内存中

👉 于是闭包就“自然产生了”

English Explanation:

  1. When inner is defined, it captures its surrounding scope (including x)
  2. Even after outer finishes execution
  3. inner still needs access to x
  4. So JavaScript keeps x in memory

👉 This naturally results in a closure

Question 3(经典陷阱题 / Classic Trap)

中文: 下面代码会输出什么?为什么?

English: What will this code output, and why?


我的答案:2,2,2。因为我模糊的记得,setTimeout里面的即使延时时间是0,也会放到宏任务里面去,让for循环执行完成之后才会接着执行。拿到的i都是同一个值,所以我给出的答案是2,2,2。

但是我没有想到,我居然还没有理解for循环的执行流程。var i = 0仅仅执行一次。然后开始执行迭代,迭代的时候先检查条件,再执行循环体,再执行自增。

第一步:初始化(仅一次)

第二步:第一次迭代

  1. 检查条件i < 3 (0 < 3),结果为 true

  2. 执行循环体:遇到 setTimeout

    • 注意:它只是把回调函数往“待办任务”里一扔,说:“等会儿再打印 i”。它并没有去读 i 的具体值。
  3. 执行自增i++。此时 i 变成了 1

第三步:第二次迭代

  1. 检查条件i < 3 (1 < 3),结果为 true
  2. 执行循环体:又一个 setTimeout 回调被丢进待办任务。
  3. 执行自增i++。此时 i 变成了 2

第四步:第三次迭代

  1. 检查条件i < 3 (2 < 3),结果为 true
  2. 执行循环体:第三个 setTimeout 回调被丢进待办任务。
  3. 执行自增i++。此时 i 变成了 3

第五步:循环终点

  1. 检查条件i < 3 (3 < 3),结果为 false
  2. 跳出循环:循环结束,i 停留在 3

分析:

  1. i = 0for 循环开始,执行 setTimeout

    • 注意setTimeout 的回调函数并没有执行,它被丢进了宏任务队列等待。
  2. i = 1:重复,又一个回调被丢进队列。

  3. i = 2:重复,第三个回调被丢进队列。

  4. i = 3循环结束条件达成。此时,因为你使用的是 vari 并没有被销毁,而是作为全局变量停留在了 3

当同步代码(循环)全部跑完后,主线程空闲了,事件循环(Event Loop)开始把任务队列里的回调拿出来执行:

  1. 执行第 1 个回调:console.log(i)。此时它去寻找 i,发现全局作用域下的 i 已经是 3 了。
  2. 执行第 2 个回调:console.log(i)。发现 i 还是 3
  3. 执行第 3 个回调:console.log(i)。发现 i 还是 3

 

核心原因

 

中文标准答案:

因为 var 的作用域是函数级的,循环中只有一个 i 变量。 setTimeout 的回调函数都引用同一个 i 当异步执行时,循环已经结束,此时 i === 3,所以输出 3 次 3。

闭包保存的是“变量的引用”,不是“变量的快照”。

 

English Standard Answer:

Because var is function-scoped, there is only one shared i variable. All setTimeout callbacks reference the same i. By the time the callbacks execute, the loop has finished and i === 3, so it prints 3 three times.

A closure captures a reference to a variable, not a snapshot of its value.


为什么使用let来定义i,结果不同呢?到底哪里不同呢?

 

先说明,还是存在闭包,只不过由于let会产生块级作用域,所以回调函数闭包捕获的是3个完全不同的变量。

使用 let 之后,输出会变成 0, 1, 2。这背后的核心差异在于 “作用域的创建次数”“变量的绑定方式”

1. var 是“单间”,let 是“公寓”

2. 执行过程的微观拆解

当执行 for (let i = 0; i < 3; i++) 时,逻辑是这样的:

  1. 第一轮 (i=0):

    • 创建一个作用域块(我们叫它 Block_0),在这个块里声明 let i = 0
    • setTimeout 的回调被丢进队列,但它捕获的是 Block_0 里的这个 i
  2. 第二轮 (i=1):

    • JS 引擎会帮你在后台把上一轮的值传给这一轮。
    • 创建一个的作用域块 Block_1,声明 let i = 1
    • setTimeout 的回调捕获的是 Block_1 里的这个 i
  3. 第三轮 (i=2):

    • 创建 Block_2,声明 let i = 2
    • 回调捕获的是 Block_2 里的 i

3. 本质区别:闭包捕获了不同的变量

形象比喻:

总结

let 的魔法在于:它在每次迭代时都重新声明了一个变量。这在底层其实是 JS 编译器自动帮你做了一层类似“函数传参”的包装。

你现在明白为什么在实现 MyPromise 内部逻辑时,我们更倾向于用 letconst 这种块级作用域来防止数据污染了吗?要不要试着解释一下,如果我们在 Promise.myAll 里用 var 来计数会发生什么?

Question 4(进阶核心)

中文: 闭包会导致内存泄漏吗?如果会,是为什么?如果不会,是在什么情况下会发生?

English: Can closures cause memory leaks in JavaScript? If yes, why? If not, under what conditions can it happen?

 

答案:

中文: 闭包本身不会导致内存泄漏,但使用不当的闭包可能导致内存泄漏

English: Closures themselves do not cause memory leaks, but improper use of closures can lead to memory leaks.

中文: 因为闭包会让函数持续引用外部变量,导致这些变量无法被垃圾回收(GC)释放

English: Because closures keep references to outer variables, those variables cannot be garbage collected as long as they are still referenced.

中文:

内存泄漏的本质是:不再需要的数据仍然被引用

如果闭包不再被引用,它和它引用的变量都会被回收 → 不会内存泄漏。

English:

The essence of a memory leak is: Data that is no longer needed is still being referenced If the closure is no longer referenced, both the closure and its captured variables will be garbage collected → no memory leak.

 

如何避免内存泄漏:

中文:

  1. 及时解除引用(设为 null)
  2. 清理定时器(clearInterval / clearTimeout)
  3. 移除事件监听
  4. 避免不必要的闭包持有大对象

English:

  1. Remove references (set to null)
  2. Clear timers
  3. Remove event listeners
  4. Avoid closures holding large objects unnecessarily

 

Question 5(高级 / 本质理解)

中文: 闭包和作用域链(scope chain)有什么关系?它们是同一个东西吗?

English: What is the relationship between closures and the scope chain? Are they the same thing?

 

答案:

中文: 闭包和作用域链不是同一个东西,但闭包是基于作用域链产生的结果

English: Closures and the scope chain are not the same thing, but closures are a result of the scope chain.

中文: 作用域链是 JavaScript 查找变量的一种机制:

English: The scope chain is the mechanism JavaScript uses to resolve variables:

还可以结合实际例子来解释:

中文解释:

👉 这就形成了闭包

English Explanation:

👉 This results in a closure

 

本质区别:

概念本质
作用域链查找变量的机制
闭包作用域被“保留下来”的现象

中文总结:

English Summary:

一句话答案:

中文: 闭包是由于函数在执行后仍然通过作用域链访问变量,从而导致作用域无法被释放的现象。

English: A closure occurs when a function continues to access variables via the scope chain after its outer function has finished execution.

 

 

Question 6(压轴题 / 实战 + 设计能力)

中文: 闭包在实际开发中有什么应用?请举 2-3 个真实场景(不要只说“缓存变量”这种空话)。

English: What are real-world use cases of closures? Give 2–3 practical examples (avoid vague answers like “caching variables”).

 

注意针对react来说明即可,场景有很多,但是很难理解,如果不理解,就问AI。

1、自定义 Hook (封装逻辑)

闭包允许自定义 Hook 维持私有状态并暴露方法给组件使用。

2、函数式更新(解决闭包陈旧问题)

为了避开闭包捕获旧状态的问题,React 提供了一种不依赖闭包变量的更新方式:

中文: 函数式更新可以避免闭包捕获旧值的问题。

English: Functional updates avoid stale closures by always using the latest state.

3、useEffect 捕获状态

这是闭包最直观的应用。当你使用 useEffect 时,它内部定义的函数会“捕获”当前那一次渲染中的变量。

中文: 因为 useEffect 的回调函数形成了闭包,它捕获的是初始 render 时的 count,而不是最新值。

English: The callback inside useEffect forms a closure that captures the initial value of count, not the updated one.

 

“闭包是指有权访问另一个函数作用域中变量的函数。在 React 中,它的应用非常广泛,比如 useEffect 或回调函数都会捕获当前的 stateprops。它的核心价值在于持久化局部变量的状态,但也容易导致‘陈旧闭包’问题(比如异步操作拿不到最新的 state),这时我们需要通过 useRefsetState 的函数式更新来解决。”

 

 

Question 7 - ( 为什么 React 不自动帮你解决 stale closure 问题?)

在 React 中,闭包主要用于状态的捕获和隔离。每次组件 render 时,都会生成新的函数,这些函数通过闭包绑定当前的 state。这既是 React 能够实现函数式组件的基础,也是 stale state 等问题产生的原因。

那react是怎么处理的呢?

中文: React 不解决 stale closure,是因为闭包是 JavaScript 的语言特性,同时 React 需要保持 render 的纯函数特性。如果自动更新闭包,会导致性能问题和不可预测行为。因此 React 选择通过依赖数组和函数式更新等机制,把控制权交给开发者。

English: React does not fix stale closures because closures are a JavaScript language feature, and React must preserve the purity of renders. Automatically updating closures would cause performance issues and unpredictable behavior. Instead, React provides tools like dependency arrays and functional updates to give developers control.

 

 

this指向

Question 1(基础核心 / Fundamental)

中文: 请你解释一下 JavaScript 中的 this 是什么?它是如何确定的?

English: What is this in JavaScript, and how is it determined?

 

中文答案:

this 可以理解为函数运行时的上下文对象,它指向谁不是在定义的时候决定的,而是在调用的时候决定的。 一般来说要看函数是怎么被调用的,比如普通函数调用、对象调用、call/apply/bind,或者 new 调用,不同方式 this 会不一样。

English 口语版(自然表达)

this refers to the execution context of a function. Its value is not determined when the function is defined, but when it is called. It depends on how the function is invoked, such as normal calls, method calls, or using call/apply/bind.

 

如果面试官有追问,那么就按照下面4种情况来详细说明:

可以简单分四种情况: 第一种是默认绑定,也就是普通函数直接调用,在非严格模式下 this 指向 window,严格模式下是 undefined。 第二种是隐式绑定,也就是通过对象调用,比如 obj.fn(),this 指向这个对象本身。 第三种是显式绑定,就是通过 call、apply 或 bind 去指定 this,这种情况下 this 就指向你传进去的那个对象。 第四种是 new 绑定,如果用 new 去调用函数,this 会指向新创建的实例对象。

There are mainly four cases: For a normal function call, this is window in non-strict mode or undefined in strict mode. For method calls, this refers to the object that calls the function. With call, apply, or bind, this is explicitly set to the provided object. And with new, this refers to the newly created instance.

 

 

Question 2 - (执行结果是什么)

输出什么?为什么?


分析:

这一题的本质(你必须理解):this 丢失(this loss)

 

this指向,有调用时决定,所以这里返回undefined。

中文标准口语版:

这里会输出 undefined。因为虽然这个函数最初是定义在 obj 里的,但在赋值给 fn 之后,是以普通函数的方式调用的。 根据默认绑定规则,this 在非严格模式下会指向 window,而 window 上没有 name 这个属性,所以是 undefined。

这是一个典型的 this 丢失问题。

English 口语版:

It outputs undefined. Even though the function is defined inside the object, once it's assigned to fn, it is called as a normal function. According to the default binding rule, this refers to window in non-strict mode, and since window doesn't have a name property, it prints undefined.

This is a classic case of this being lost.

 

Question 3(进阶)

中文: 那你怎么解决 this 丢失问题?

English: How would you fix the this binding issue in this case?

 

中文口语版

this 丢失一般有几种解决方式。 第一种是使用 bind 显式绑定 this;bind 会返回一个新的函数,并且把 this 永久绑定住。 第二种是用箭头函数,因为箭头函数没有自己的 this,会继承外层的 this; 还有一种是在调用的时候用 call 或 apply 指定 this。call 和 apply 是在调用时临时指定 this。

 

解决 this 丢失的本质是:确保函数调用时的 this 是我们期望的对象

English 口语版

There are a few ways to fix the this binding issue. One is using bind to explicitly bind this.bind returns a new function with this permanently bound. Another is using arrow functions, since they don’t have their own this and inherit it from the outer scope. You can also use call or apply to explicitly set this when invoking the function.call and apply set this at invocation time.

 

Fixing this issues is essentially about ensuring the function is called with the correct context.

 

Question 4(高频 + React 强相关)

中文: 箭头函数的 this 和普通函数的 this 有什么区别?

English: What is the difference between this in arrow functions and regular functions?

 

中文高分表达

箭头函数和普通函数最大的区别在于 this 的绑定方式不同。 普通函数的 this 是在调用时决定的,取决于调用方式; 而箭头函数没有自己的 this,它的 this 是在定义时就确定的,会继承外层作用域的 this。

English 口语版

The main difference is how this is determined. For regular functions, this is decided at call time depending on how the function is invoked. For arrow functions, this is lexically bound, meaning it is determined at definition time and inherited from the outer scope.

 

事件循环 Event Loop

Question 1(基础核心 / Fundamental)

中文: 什么是事件循环(Event Loop)?你可以用自己的话解释一下。

English: What is the Event Loop in JavaScript? Explain it in your own words.

 

中文(推荐直接说)

事件循环可以理解为 JavaScript 处理异步任务的一种机制。 因为 JavaScript 是单线程的,它一次只能做一件事情,所以当遇到异步任务时,比如 setTimeout 或 Promise,不会立即执行,而是先放到对应的队列里。 等主线程的同步任务执行完之后,事件循环会不断地从任务队列中取任务出来执行,这样就实现了异步。

 

可以简单理解为:事件循环就是不断检查任务队列并执行任务的过程。

English(口语版)

The event loop is a mechanism that JavaScript uses to handle asynchronous tasks. Since JavaScript is single-threaded, it can only do one thing at a time. So when it encounters async tasks like setTimeout or Promise, they are placed into queues instead of being executed immediately. Once the main thread finishes its current tasks, the event loop continuously picks tasks from the queue and executes them.

 

You can think of it as a loop that keeps checking the task queue and executing tasks.

 

 

Question 2(核心理解 / 必考)

中文: 什么是宏任务(macro task)和微任务(micro task)?它们有什么区别?

English: What are macro tasks and micro tasks? What is the difference between them?

 

中文口语版

宏任务和微任务都是异步任务,只是优先级不同。 微任务的优先级比宏任务更高,会在当前宏任务执行完之后立即执行。

 

常见的宏任务有:setTimeout、setInterval、还有整体的 script 代码。

常见的微任务有:Promise 、 MutationObserver。

每执行完一个宏任务,都会立即清空所有微任务队列,然后才会执行下一个宏任务。

宏任务是一个个执行的,而每个宏任务之间都会先执行完所有微任务。

English 口语版

Macro tasks and micro tasks are both asynchronous tasks, but they have different priorities. Micro tasks have higher priority and are executed immediately after the current macro task.

 

Common macro tasks include setTimeout, setInterval, and the overall script execution.

Common micro tasks include Promise and MutationObserver.

After each macro task, all micro tasks are executed before moving on to the next macro task.

Macro tasks are executed one by one, and between them, all micro tasks are completed first.

 

重要理念澄清:

宏任务包括setTimeout、setInterval,但是还包括整体的script代码。但是整体的script代码是同步的,而setTimeout、setInterval是异步的,所以执行顺序是先执行同步的宏任务,然后清空微任务,再执行异步的宏任务。

所以这里还有一个重要的点,就是同步、异步。

 

 

Question 3(经典执行顺序题)

中文: 下面代码输出什么?

English: What is the output of this code?

 

我的答案:1,4,3,2。完全正确。

中文口语版

这段代码会先执行同步代码,所以先输出 1 和 4。 然后 Promise.then 是微任务,会在当前宏任务结束后立即执行,所以输出 3。 setTimeout 是宏任务,会在下一轮事件循环中执行,所以最后输出 2。

English 口语版

First, the synchronous code runs, so it prints 1 and 4. Then, the Promise.then is a micro task, which runs right after the current macro task, so it prints 3. Finally, setTimeout is a macro task, so it runs in the next event loop, printing 2.

 

Question 4(进阶陷阱)

中文: 下面代码输出什么?

English: What is the output of this code?

 

我的答案:1,5,4,2,3。完全正确。

中文版(推荐直接说)

先执行同步代码,所以先输出 1 和 5。 然后 Promise.then 是微任务,会在当前宏任务结束后执行,所以输出 4。 接着进入下一轮宏任务,执行 setTimeout,先输出 2。 在这个 setTimeout 里面又产生了一个 Promise.then,这是微任务,所以会在当前宏任务结束后立即执行,输出 3。

English 版

First, the synchronous code runs, so it prints 1 and 5. Then, the Promise.then is a micro task, so it runs after the current macro task and prints 4. Next, the setTimeout callback runs as a macro task and prints 2. Inside that callback, another Promise.then is scheduled, which is a micro task, so it runs immediately after and prints 3.

 

 

原型 & 原型链

Question 1(基础核心 / Fundamental)

中文: 什么是原型(prototype)?什么是原型链(prototype chain)?

English: What is a prototype, and what is the prototype chain in JavaScript?

 

中文(推荐直接说)

原型可以理解为 JavaScript 对象之间共享属性和方法的一种机制。 每个对象都有一个隐式的原型,也就是 prototype,通过这个原型可以访问它没有定义的属性或方法。 而原型链就是由一个对象通过它的原型,一层一层往上查找,直到找到 null 为止的这个结构。

 

JavaScript 查找属性的过程,本质就是原型链的查找过程。

English(口语版)

A prototype is a mechanism in JavaScript that allows objects to share properties and methods. Every object has an internal prototype, which it can use to access properties or methods that are not defined on itself. The prototype chain is the structure formed when an object keeps looking up through its prototype until it reaches null.

 

Property lookup in JavaScript is essentially a traversal of the prototype chain.

 

Question 2(核心理解)

中文: 构造函数、实例对象、prototype 之间是什么关系?

English: What is the relationship between constructor function, instance, and prototype?

 

中文(推荐直接说)

在 JavaScript 中,构造函数、实例对象和 prototype 之间是一个关联结构。 构造函数有一个 prototype 属性,这个 prototype 上可以挂共享的方法。 当我们用 new 调用构造函数时,会创建一个实例对象,这个实例对象内部会有一个隐式属性指向构造函数的 prototype。 所以实例对象可以通过这个原型访问到构造函数 prototype 上的方法。

English(口语版)

In JavaScript, constructor functions, instances, and prototypes are connected. A constructor function has a prototype property where shared methods are defined. When we use new, an instance is created, and its internal prototype points to the constructor’s prototype. This allows the instance to access methods defined on the prototype.

 

Question 3(必考 / 分水岭)

中文: __proto__prototype 有什么区别?

English: What is the difference between __proto__ and prototype?

 

中文(推荐直接说)

prototype 是函数才有的属性,它用来定义构造函数创建出来的实例的共享方法。 proto 是对象上的一个隐式属性,它指向创建这个对象的构造函数的 prototype。 简单来说,prototype 是“模板”,proto 是“链接”,用于构成原型链。 一般开发中不推荐直接使用 proto,更推荐使用 Object.getPrototypeOf。

English(口语版)

prototype is a property of functions, used to define shared methods for instances created by the constructor. __proto__ is an internal reference on objects that points to the constructor’s prototype. Simply put, prototype is the “template”, and __proto__ is the “link” that forms the prototype chain. In practice, __proto__ is not recommended, and Object.getPrototypeOf is preferred.


深入理解:

1️⃣ prototype(函数有)

👉 用来“提供共享方法”

2️⃣ proto(对象有)

👉 用来“指向原型”

 

 

Question 4(高频 + 深度理解)

中文: 如果一个对象找不到某个属性,会发生什么?原型链是怎么工作的?

English: What happens when an object cannot find a property? How does the prototype chain work?

 

中文(推荐直接说)

当一个对象访问某个属性时,如果它本身没有这个属性,JavaScript 就会沿着它的原型链向上查找。 也就是先找自己的属性,如果没有,就去它的 proto 指向的原型对象上找,再没有就继续往上找,直到找到 Object.prototype。 如果一直找不到,最终返回 undefined。

English(口语版)

When an object tries to access a property, JavaScript first looks for it on the object itself. If it’s not found, it goes up the prototype chain via __proto__, then checks the prototype object, and keeps going up. This continues until it reaches Object.prototype. If the property is still not found, it returns undefined.


深入理解:

你可以这样理解(面试可以不说这么细,但要懂):

查找顺序:

举个例子:

查找过程:

  1. p 自己没有 sayHi
  2. 去 p.proto(Person.prototype)
  3. 找到了 sayHi
  4. 直接执行

 

 

作用域 + 变量提升(Scope & Hoisting)

Question 1(基础核心 / Fundamental)

中文: 什么是作用域(Scope)?JavaScript 中有哪些作用域?

English: What is scope in JavaScript? What types of scope are there?

 

中文(推荐直接说)

作用域可以理解为变量和函数的可访问范围,也就是在代码中哪些地方可以访问某个变量。 JavaScript 中主要有三种作用域:全局作用域、函数作用域,以及 ES6 之后引入的块级作用域。

 

作用域本质是 JavaScript 管理变量访问权限的一种机制。

English(口语版)

Scope refers to the area in code where variables and functions are accessible. In JavaScript, there are mainly three types of scope: global scope, function scope, and block scope introduced in ES6.

 

Scope is essentially a mechanism for managing variable accessibility in JavaScript.

三种作用域(面试必须会说)

1️⃣ 全局作用域

👉 整个文件都能访问

2️⃣ 函数作用域

👉 只在函数内部有效

3️⃣ 块级作用域(ES6)

👉 只在 {} 内有效

 

 

Question 2(核心理解 / 高频)

中文: 什么是作用域链(Scope Chain)?它是如何工作的?

English: What is the scope chain, and how does it work?

 

中文(推荐直接说)

作用域链可以理解为:当访问一个变量时,如果在当前作用域找不到,就会沿着它的外层作用域一层一层往上查找,直到全局作用域,如果还找不到就会报错。 这种从内到外逐级查找的结构,就叫作用域链。

 

作用域链的本质是“变量查找机制”,从当前作用域逐级向上查找。

English(口语版)

The scope chain is the mechanism where JavaScript looks up variables. If a variable is not found in the current scope, it will search in the outer scope step by step until it reaches the global scope. If it is still not found, an error is thrown. This chain of nested scopes is called the scope chain.

 

The scope chain is essentially a variable lookup mechanism from inner to outer scopes.


深入理解:

 

查找过程:

例子:

查找过程:

image-20260412101659180


常见误区:

 

❌ 错误理解:

作用域链是“函数之间的关系”

✔ 正确理解:

作用域链是“变量查找路径”

 

 

Question 3(经典必考 / Hoisting)

中文: 什么是变量提升(Hoisting)?

English: What is hoisting in JavaScript?

 

中文

变量提升是指在 JavaScript 执行代码之前,会先把变量和函数的声明提升到当前作用域的顶部。 对于 var 来说,变量会被提升,但只提升声明,不提升赋值,所以在赋值之前访问会得到 undefined。 而函数声明会整体提升。

 

变量提升发生在 JavaScript 的编译阶段,而不是执行阶段。

English(口语版)

Hoisting means that during the creation phase, JavaScript moves variable and function declarations to the top of their scope before execution. For var, only the declaration is hoisted, not the assignment, so accessing it before initialization returns undefined. Function declarations are fully hoisted.

 

Hoisting happens during the compilation phase, not the execution phase.


深入理解:

1️⃣ var 的提升

等价于:

2️⃣ 函数提升(更强)

✔ 可以正常执行

 

 

Question 4(高频陷阱)

中文: let 和 const 有变量提升吗?为什么会出现暂时性死区(TDZ)?

English: Do let and const have hoisting? What is the Temporal Dead Zone (TDZ)?

 

中文(推荐直接说)

let 和 const 也是存在变量提升的,但是它们的提升不会让变量在声明之前可以被访问。 在代码执行到声明之前,这一段区域叫暂时性死区(TDZ),在 TDZ 里面访问变量会直接报错。 所以本质上是“提升了,但不可用”。

English(口语版)

let and const are also hoisted, but they are not initialized before execution reaches their declaration. The area before the declaration is called the Temporal Dead Zone (TDZ), and accessing the variable there will cause a ReferenceError. So they are hoisted, but not accessible before initialization.